package service

import (
	"errors"
	"fmt"
	"gorm.io/gorm"
	"math/rand"
	dao2 "miao-wails/backed/app/dao"
	"miao-wails/backed/global"
	"miao-wails/backed/lib/id"
	"miao-wails/backed/lib/utils"
	"miao-wails/backed/structs/base"
	"miao-wails/backed/structs/tables"
	"os"
	"path/filepath"
	"time"
)

var TarSer = new(tarService)

type tarService struct{}

// TarFile 归档文件
func (s tarService) TarFile(fileStr string) (err error) {
	return s.tarFile(fileStr)
}

// GetFileList 获取文件列表
func (s tarService) GetFileList(in base.SearchTarFile) (li []tables.TarFile, err error) {
	li, err = dao2.TarFileDao.FindList(in)
	return
}

// DelFile 删除文件
func (s tarService) DelFile(fid int64) (err error) {
	var (
		m tables.TarFile
	)
	m, err = dao2.TarFileDao.FindById(fid)
	if err != nil {
		return err
	}
	// 先删除文件，然后在删除记录
	err = os.Remove(filepath.Join(global.Conf.DataDir, base.TarFileFolder, fmt.Sprintf("%s%s", m.Md5, base.TarFileSaveSuffix)))
	if err != nil {
		return err
	}
	err = dao2.TarFileDao.DelById(m.Id)
	return err
}

// UpdateFileInfo 更新文件信息
func (s tarService) UpdateFileInfo(in base.UpdateTarFileInfo) (err error) {
	var (
		m tables.TarFile
	)
	m, err = dao2.TarFileDao.FindById(utils.StrToInt64(in.Id))
	if err != nil {
		return err
	}
	m.Name = in.Name
	m.Description = in.Description
	m.DocType = in.DocType
	m.Label = in.Label
	m.DocSuffix = in.DocSuffix
	err = dao2.TarFileDao.UpdateById(m)
	return err
}

// ChangeFilePrivate 改变文件的私有属性
func (s tarService) ChangeFilePrivate(fid int64) (err error) {
	var (
		m tables.TarFile
	)
	m, err = dao2.TarFileDao.FindById(fid)
	if err != nil {
		return err
	}
	m.IsPrivate = !m.IsPrivate
	err = dao2.TarFileDao.UpdateById(m)
	return err
}

//////////////////////////////////////////

// ChangePrivate 改变歌曲的私有属性
func (s tarService) ChangePrivate(idStr string) (err error) {
	musId := utils.StrToInt(idStr)
	var m tables.TarMusic
	err = dao2.DB.Model(&tables.TarMusic{}).Where("id = ?", musId).Find(&m).Error
	if err != nil {
		return err
	}
	m.IsPrivate = !m.IsPrivate
	err = dao2.DB.Save(&m).Error
	return
}

func (s tarService) DeleteMusic(idStr string) (err error) {
	musId := utils.StrToInt(idStr)
	var m tables.TarMusic
	err = dao2.DB.Model(&tables.TarMusic{}).Where("id = ?", musId).Find(&m).Error
	if err != nil {
		return err
	}
	err = os.Remove(s.getMusicTarFile(m.Path))
	if err != nil {
		return err
	}
	err = dao2.DB.Where("id = ?", musId).Delete(&m).Error
	return err
}

func (s tarService) ChangeLoop() (str string, err error) {
	modes := []string{"one", "loop", "random"}
	index := utils.GetIndexStringSlice(global.Conf.ModuleConfig.MusicLoopMode, modes)
	index++
	str = modes[index%len(modes)]
	err = dao2.TarFileDao.UpdateCurrentMusicLoopMode(str)
	return
}

// GetSongInfo 获取歌曲信息，whereStr 可选：curr、next、last、指定歌曲的id
func (s tarService) GetSongInfo(whereStr string) (m tables.TarMusic, err error) {
	var mid int64
	switch whereStr {
	case "curr":
		mid = utils.StrToInt64(global.Conf.ModuleConfig.MusicCurrentId)
	case "next":
		fallthrough
	case "last":
		var idList []int64
		err = dao2.DB.Raw("select id from tar_music ").Scan(&idList).Error
		if err != nil {
			return
		}
		if len(idList) == 0 {
			err = fmt.Errorf("歌曲库为空！")
			return
		}
		if global.Conf.ModuleConfig.MusicLoopMode == "random" {
			mid = idList[rand.Intn(len(idList))]
		} else {
			idIndex := utils.GetIndexInt64Slice(utils.StrToInt64(global.Conf.ModuleConfig.MusicCurrentId), idList)
			if whereStr == "next" {
				idIndex++
			} else {
				idIndex--
			}
			// 防止越界
			if idIndex >= len(idList) {
				idIndex = 0
			}
			if idIndex <= 0 {
				idIndex = len(idList) - 1
			}
			mid = idList[idIndex]
		}
	default:
		mid = utils.StrToInt64(whereStr)
	}
	err = dao2.DB.Model(&tables.TarMusic{}).Where("id = ?", mid).Find(&m).Error
	if err != nil {
		return
	}
	err = dao2.TarFileDao.UpdateCurrentMusicId(mid)
	if err != nil {
		return
	}
	if m.Id != 0 {
		m.Path = "http://localhost:9966/media/music/" + m.Path
		return
	}
	err = dao2.DB.Model(&tables.TarMusic{}).First(&m).Error
	if errors.Is(err, gorm.ErrRecordNotFound) {
		err = fmt.Errorf("歌曲库为空！")
		return
	}
	m.Path = "http://localhost:9966/media/music/" + m.Path
	return
}

func (s tarService) GetMusicList(showPrivate bool) (li []tables.TarMusic, err error) {
	tx := dao2.DB.Model(&tables.TarMusic{})
	if !showPrivate {
		tx.Where("is_private = ?", false)
	}
	err = tx.Find(&li).Error
	return
}

func (s tarService) MusicTar(folderPath string) (err error) {
	// 判断是文件还是目录
	// 解析文件信息，将文件复制到应用目录下
	// 返回新增的文件目录
	var (
		info os.FileInfo
	)
	info, err = os.Stat(folderPath)
	if err != nil {
		return
	}
	if info.IsDir() {
		var entries []os.DirEntry
		entries, err = os.ReadDir(folderPath)
		if err != nil {
			return
		}
		for _, entry := range entries {
			if !entry.IsDir() {
				err = s.musicTarFile(filepath.Join(folderPath, entry.Name()))
				if err != nil {
					return
				}
			}
		}
	} else {
		err = s.musicTarFile(folderPath)
	}
	return
}

// musicTarFile 检查文件是否已经在归档目录中，如果不存在就复制文件，然后写入数据库
func (s tarService) musicTarFile(mf string) (err error) {
	var (
		md5   string
		m     tables.TarMusic
		fName string
		fSuf  string
	)
	md5 = utils.GetFileMD5(mf)
	fName = utils.GetFileName(mf)
	fSuf = utils.GetFileSuffix(mf)
	// 检查这个文件是否已经在归档目录中
	b := utils.IsExistFile(s.getMusicTarFile(fmt.Sprintf("%s_%s.%s", fName, md5, fSuf)))
	if b {
		return
	}
	err = dao2.DB.Model(&tables.TarMusic{}).Where("md5 = ?", md5).Find(&m).Error
	if err != nil {
		return
	}
	if m.Id != 0 {
		return
	}
	err = utils.CopyFile(mf, s.getMusicTarFile(fmt.Sprintf("%s_%s.%s", fName, md5, fSuf)))
	if err != nil {
		return
	}
	m = tables.TarMusic{
		Id:        id.GetSnowId(),
		Name:      fName,
		Path:      fmt.Sprintf("%s_%s.%s", fName, md5, fSuf),
		Singer:    "",
		Label:     "",
		Duration:  0,
		CreateAt:  time.Now().Unix(),
		Md5:       md5,
		IsPrivate: false,
	}
	err = dao2.DB.Save(&m).Error
	return
}

// getMusicTarFile 获取归档文件的物理路径
func (s tarService) getMusicTarFile(fName string) (str string) {
	return filepath.Join(global.Conf.DataDir, base.TarMusicFolder, fName)
}

// TarVideo 归档视频文件,视频需要拆解
func (s tarService) TarVideo(fp string) (err error) {
	return
}

// tarFile 归档一般文件，将文件名称修改为文件MD5，同时将后缀名调整为 hpd,避免被其他工具解析
func (s tarService) tarFile(fp string) (err error) {
	var (
		md5   string
		m     tables.TarFile
		fName string
		fSuf  string
	)
	md5 = utils.GetFileMD5(fp)
	fName = utils.GetFileName(fp)
	fSuf = utils.GetFileSuffix(fp)
	m, err = dao2.TarFileDao.FindByMd5(md5)
	if err != nil {
		return
	}
	if m.Id != 0 {
		// 说明文件已经存在，跳过归档操作
		return
	}
	// 拷贝文件
	err = utils.CopyFile(fp, filepath.Join(global.Conf.DataDir, base.TarFileFolder, fmt.Sprintf("%s%s", md5, base.TarFileSaveSuffix)))
	if err != nil {
		return
	}
	// 写入数据库
	m = tables.TarFile{
		Id:            id.GetSnowId(),
		Name:          fName,
		Md5:           md5,
		DocType:       s.getTarFileType(fSuf),
		DocSuffix:     fSuf,
		IsPrivate:     false,
		Label:         "",
		Description:   "",
		ExtensionJson: "",
		CreateAt:      time.Now().Unix(),
	}
	err = dao2.TarFileDao.Create(&m)
	return err
}

// getTarFileType 获取文件类型
func (s tarService) getTarFileType(fSuf string) (tStr string) {
	switch fSuf {
	case "mp4":
		tStr = tables.DocTypeForVideo
	default:
		tStr = tables.DocTypeForTar
	}
	return
}
